
/**
  ******************************************************************************
  * Copyright 2021 The grapilot Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       gp_serial.cpp
  * @author     baiyang
  * @date       2021-7-19
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "gp_com.h"
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
WSerial::WSerial()
{
    //初始化变量
    memset(&m_OverlappedRead, 0, sizeof(OVERLAPPED));
    memset(&m_OverlappedWrite, 0, sizeof(OVERLAPPED));

    m_hIDComDev = NULL;
    m_bOpened = FALSE;
}

WSerial::~WSerial()
{
    Close();
}

BOOL WSerial::Open(int nPort, int nBaud)
{
    if (m_bOpened)
    {
        return TRUE;
    }
    TCHAR sPort[15];      //端口信息
    TCHAR sComParams[50]; //串口配置信息

    if (nPort < 10) {
        wsprintf(sPort, TEXT("COM%d"), nPort); //将串口逻辑名写入szPort
    } else {
        wsprintf(sPort, TEXT("\\\\.\\COM%d"), nPort); //将串口逻辑名写入szPort
    }

    //重叠IO打开串口
    m_hIDComDev = CreateFile(sPort,
        GENERIC_READ | GENERIC_WRITE,  //允许读和写
        0,                             //独占方式
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL , //重叠方式 FILE_FLAG_OVERLAPPED
        NULL);
    if (m_hIDComDev == INVALID_HANDLE_VALUE)
    {
        //创建失败
        //AfxMessageBox("打开COM失败!");
        return FALSE;
    }

    memset(&m_OverlappedRead, 0, sizeof(OVERLAPPED));
    memset(&m_OverlappedWrite, 0, sizeof(OVERLAPPED));

    //配置串口
    //SetupComm(m_hIDComDev, 10000, 10000);    //设置缓冲区（输入输出）
    //设置超时(未设置超时时间)
    COMMTIMEOUTS TimeOuts;
    TimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
    TimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
    TimeOuts.ReadTotalTimeoutMultiplier = 0;
    TimeOuts.ReadTotalTimeoutConstant = 0;
    TimeOuts.WriteTotalTimeoutMultiplier = 0;
    TimeOuts.WriteTotalTimeoutConstant = 5000;
    SetCommTimeouts(m_hIDComDev, &TimeOuts); //设置超时

    wsprintf(sComParams, TEXT("COM%d:%d,n,8,1"), nPort, nBaud);
    m_OverlappedRead.hEvent  = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    DCB dcb;
    GetCommState(m_hIDComDev, &dcb);
    dcb.BaudRate = nBaud;
    dcb.ByteSize = 8;
    dcb.fDtrControl = DTR_CONTROL_ENABLE;
    dcb.Parity = NOPARITY; //无奇偶校验位

    unsigned char ucSet;
    ucSet = (unsigned char)((FC_RTSCTS & FC_DTRDSR) != 0);
    ucSet = (unsigned char)((FC_RTSCTS & FC_RTSCTS) != 0);
    ucSet = (unsigned char)((FC_RTSCTS & FC_XONXOFF) != 0);

    if (!SetCommState(m_hIDComDev, &dcb) ||
        !SetupComm(m_hIDComDev, 1024, 1024) ||
        m_OverlappedRead.hEvent == NULL ||
        m_OverlappedWrite.hEvent == NULL)
    {
        DWORD dwError = GetLastError();
        if (m_OverlappedRead.hEvent != NULL)
        {
            CloseHandle(m_OverlappedRead.hEvent);
        }
        if (m_OverlappedWrite.hEvent != NULL)
        {
            CloseHandle(m_OverlappedWrite.hEvent);
        }
        CloseHandle(m_hIDComDev);
        return FALSE;
    }
    //清除缓冲区
    PurgeComm(m_hIDComDev, PURGE_TXCLEAR | PURGE_RXCLEAR);
    m_bOpened = TRUE;
    return     m_bOpened;
}

BOOL WSerial::OpenSbus(int nPort, int nBaud)
{
    if (m_bOpened)
    {
        return TRUE;
    }
    TCHAR sPort[15];      //端口信息
    TCHAR sComParams[50]; //串口配置信息

    if (nPort < 10) {
        wsprintf(sPort, TEXT("COM%d"), nPort); //将串口逻辑名写入szPort
    }
    else {
        wsprintf(sPort, TEXT("\\\\.\\COM%d"), nPort); //将串口逻辑名写入szPort
    }

    //重叠IO打开串口
    m_hIDComDev = CreateFile(sPort,
        GENERIC_READ | GENERIC_WRITE,  //允许读和写
        0,                             //独占方式
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, //重叠方式 FILE_FLAG_OVERLAPPED
        NULL);
    if (m_hIDComDev == INVALID_HANDLE_VALUE)
    {
        //创建失败
        //AfxMessageBox("打开COM失败!");
        return FALSE;
    }

    memset(&m_OverlappedRead, 0, sizeof(OVERLAPPED));
    memset(&m_OverlappedWrite, 0, sizeof(OVERLAPPED));

    //配置串口
    //SetupComm(m_hIDComDev, 10000, 10000);    //设置缓冲区（输入输出）
    //设置超时(未设置超时时间)
    COMMTIMEOUTS TimeOuts;
    TimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
    TimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
    TimeOuts.ReadTotalTimeoutMultiplier = 0;
    TimeOuts.ReadTotalTimeoutConstant = 0;
    TimeOuts.WriteTotalTimeoutMultiplier = 0;
    TimeOuts.WriteTotalTimeoutConstant = 5000;
    SetCommTimeouts(m_hIDComDev, &TimeOuts); //设置超时

    wsprintf(sComParams, TEXT("COM%d:%d,n,8,1"), nPort, nBaud);
    m_OverlappedRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_OverlappedWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    DCB dcb;
    GetCommState(m_hIDComDev, &dcb);
    dcb.BaudRate = nBaud;
    dcb.ByteSize = 8;
    dcb.fDtrControl = DTR_CONTROL_ENABLE;
    dcb.Parity = EVENPARITY; //偶校验位
    dcb.StopBits = 2;

    unsigned char ucSet;
    ucSet = (unsigned char)((FC_RTSCTS & FC_DTRDSR) != 0);
    ucSet = (unsigned char)((FC_RTSCTS & FC_RTSCTS) != 0);
    ucSet = (unsigned char)((FC_RTSCTS & FC_XONXOFF) != 0);

    if (!SetCommState(m_hIDComDev, &dcb) ||
        !SetupComm(m_hIDComDev, 10000, 10000) ||
        m_OverlappedRead.hEvent == NULL ||
        m_OverlappedWrite.hEvent == NULL)
    {
        DWORD dwError = GetLastError();
        if (m_OverlappedRead.hEvent != NULL)
        {
            CloseHandle(m_OverlappedRead.hEvent);
        }
        if (m_OverlappedWrite.hEvent != NULL)
        {
            CloseHandle(m_OverlappedWrite.hEvent);
        }
        CloseHandle(m_hIDComDev);
        return FALSE;
    }
    //清除缓冲区
    PurgeComm(m_hIDComDev, PURGE_TXCLEAR | PURGE_RXCLEAR);
    m_bOpened = TRUE;
    return     m_bOpened;
}

BOOL WSerial::Close(void)
{
    if (!m_bOpened || m_hIDComDev == NULL)
    {
        return TRUE;
    }

    if (m_OverlappedRead.hEvent != NULL)
    {
        CloseHandle(m_OverlappedRead.hEvent);
    }
    if (m_OverlappedWrite.hEvent != NULL)
    {
        CloseHandle(m_OverlappedWrite.hEvent);
    }
    CloseHandle(m_hIDComDev);
    m_bOpened = FALSE;
    m_hIDComDev = NULL;

    return TRUE;
}

BOOL WSerial::WriteCommByte(unsigned char ubyte)
{
    BOOL bWriteStat;
    DWORD dByteWritten;

    bWriteStat = WriteFile(m_hIDComDev, (LPSTR)&ubyte, 1, &dByteWritten, &m_OverlappedWrite);

    //重叠操作还未完成
    if (!bWriteStat && (GetLastError() == ERROR_IO_PENDING))
    {
        if (WaitForSingleObject(m_OverlappedWrite.hEvent, 1000))
        {
            dByteWritten = 0;
        }
        else
        {
            GetOverlappedResult(m_hIDComDev, &m_OverlappedWrite, &dByteWritten, FALSE);
            m_OverlappedWrite.Offset += dByteWritten;
        }
    }
    return TRUE;

}

int WSerial::SendData(const char* buffer, int size)
{
    if (!m_bOpened || m_hIDComDev == NULL)
    {
        return 0;
    }

    DWORD dwByteWritten = 0;
    for (int i = 0; i < size; i++)
    {
        WriteCommByte(buffer[i]);
        dwByteWritten++;
    }
    return ((int)dwByteWritten);
}

int WSerial::ReadDataWaiting(void)
{
    if (!m_bOpened || m_hIDComDev == NULL)
    {
        return 0;
    }
    DWORD dwErrorFlags;
    COMSTAT ComStat;

    //清除错误
    ClearCommError(m_hIDComDev, &dwErrorFlags, &ComStat);
    //返回缓冲区字节数
    return ((int)ComStat.cbInQue);
}

int WSerial::ReadData(void* buffer, int limit)
{
    if (!m_bOpened || m_hIDComDev == NULL)
    {
        return 0;
    }

    BOOL bReadStatus;
    DWORD dwBytesRead, dwErrorFlags;
    COMSTAT ComStat;

    //清除错误
    ClearCommError(m_hIDComDev, &dwErrorFlags, &ComStat);
    //如果缓冲区字节数为零 则返回
    if (!ComStat.cbInQue)
    {
        return (0);
    }

    dwBytesRead = (DWORD)ComStat.cbInQue;

    if (limit < (int)dwBytesRead) dwBytesRead = (DWORD)limit;
    bReadStatus = ReadFile(m_hIDComDev, buffer, dwBytesRead, &dwBytesRead, &m_OverlappedRead);
    if (!bReadStatus)
    {
        if (GetLastError() == ERROR_IO_PENDING)
        {
            WaitForSingleObject(m_OverlappedRead.hEvent, 2000);
            return ((int)dwBytesRead);
        }
        return 0;
    }
    return ((int)dwBytesRead);
}

HANDLE WSerial::GetSerialHandle(void)
{
    return m_hIDComDev;
}

/*------------------------------------test------------------------------------*/


